/**
 * 函数柯里化实现
 * 柯里化是将一个多参数函数转换成一系列单参数函数的过程
 */

// 基础柯里化函数
function curry(fn) {
  return function curried(...args) {
    // 如果传入的参数数量大于等于原函数参数数量，直接执行
    if (args.length >= fn.length) {
      return fn.apply(this, args);
    } else {
      // 否则返回一个新函数，继续接收剩余参数
      return function(...nextArgs) {
        return curried.apply(this, args.concat(nextArgs));
      };
    }
  };
}

// 支持占位符的柯里化函数
function curryWithPlaceholder(fn, placeholder = '_') {
  return function curried(...args) {
    // 过滤掉占位符，获取有效参数
    const validArgs = args.slice(0, fn.length);
    const hasPlaceholder = validArgs.includes(placeholder);
    
    // 如果没有占位符且参数数量足够，直接执行
    if (!hasPlaceholder && validArgs.length >= fn.length) {
      return fn.apply(this, validArgs);
    }
    
    // 否则返回新函数继续接收参数
    return function(...nextArgs) {
      const newArgs = [];
      let argIndex = 0;
      let nextIndex = 0;
      
      // 用实际参数替换占位符
      while (argIndex < args.length && newArgs.length < fn.length) {
        if (args[argIndex] === placeholder && nextIndex < nextArgs.length) {
          newArgs.push(nextArgs[nextIndex++]);
          argIndex++;
        } else {
          newArgs.push(args[argIndex++]);
        }
      }
      
      // 添加剩余的实际参数
      while (newArgs.length < fn.length && nextIndex < nextArgs.length) {
        newArgs.push(nextArgs[nextIndex++]);
      }
      
      // 如果还有占位符，继续保留
      while (argIndex < args.length && newArgs.length < fn.length) {
        newArgs.push(args[argIndex++]);
      }
      
      return curried.apply(this, newArgs);
    };
  };
}

// 无限参数柯里化（不依赖函数参数长度）
function infiniteCurry(fn) {
  return function curried(...args) {
    // 如果没有参数，返回函数本身
    if (args.length === 0) {
      return curried;
    }
    
    // 否则返回一个新函数或执行结果
    return function(...nextArgs) {
      const allArgs = args.concat(nextArgs);
      
      // 如果下一个参数是空，执行函数
      if (nextArgs.length === 0) {
        return fn.apply(this, allArgs);
      } else {
        return curried.apply(this, allArgs);
      }
    };
  };
}

// 测试用例
console.log('=== 函数柯里化测试 ===');

// 测试基础柯里化
function add(a, b, c) {
  return a + b + c;
}

const curriedAdd = curry(add);

console.log('基础柯里化测试:');
console.log('直接调用:', curriedAdd(1, 2, 3)); // 6
console.log('分步调用:', curriedAdd(1)(2)(3)); // 6
console.log('混合调用:', curriedAdd(1, 2)(3)); // 6
console.log('混合调用2:', curriedAdd(1)(2, 3)); // 6

// 测试占位符柯里化
const curriedAddWithPlaceholder = curryWithPlaceholder(add);

console.log('\n占位符柯里化测试:');
console.log('正常调用:', curriedAddWithPlaceholder(1, 2, 3)); // 6
console.log('占位符调用1:', curriedAddWithPlaceholder('_', 2)(1, 3)); // 6
console.log('占位符调用2:', curriedAddWithPlaceholder('_', '_', 3)(1)(2)); // 6
console.log('占位符调用3:', curriedAddWithPlaceholder(1, '_', 3)('_', 2)()); // 6

// 测试无限参数柯里化
function sum(...numbers) {
  return numbers.reduce((total, num) => total + num, 0);
}

const curriedSum = infiniteCurry(sum);

console.log('\n无限参数柯里化测试:');
console.log('分步累加:', curriedSum(1)(2)(3)(4)()); // 10
console.log('批量添加:', curriedSum(1, 2)(3, 4)()); // 10
console.log('空调用返回函数:', typeof curriedSum()); // 'function'

// 实际应用场景
console.log('\n=== 实际应用场景 ===');

// 场景1：参数预置
function createURL(baseURL, path, query) {
  return `${baseURL}/${path}?${new URLSearchParams(query).toString()}`;
}

const createApiURL = curry(createURL)('https://api.example.com');
const getUserURL = createApiURL('users');

console.log('API URL生成:');
console.log('用户列表:', getUserURL({ page: 1, limit: 10 }));
// https://api.example.com/users?page=1&limit=10

// 场景2：事件处理
function handleEvent(eventType, elementId, callback) {
  document.getElementById(elementId).addEventListener(eventType, callback);
}

const curriedHandleEvent = curry(handleEvent);
const handleClick = curriedHandleEvent('click');
const handleButtonClick = handleClick('myButton');

console.log('事件处理柯里化: 已创建专门的事件处理函数');

// 场景3：数据验证
function validateData(rule, value) {
  const rules = {
    email: v => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v),
    phone: v => /^1[3-9]\d{9}$/.test(v),
    required: v => v !== null && v !== undefined && v !== ''
  };
  return rules[rule] ? rules[rule](value) : false;
}

const curriedValidate = curry(validateData);
const validateEmail = curriedValidate('email');
const validatePhone = curriedValidate('phone');
const checkRequired = curriedValidate('required');

console.log('数据验证测试:');
console.log('邮箱验证:', validateEmail('test@example.com')); // true
console.log('手机号验证:', validatePhone('13800138000')); // true
console.log('必填验证:', checkRequired('')); // false

// 性能测试
console.log('\n=== 性能测试 ===');

function complexCalculation(a, b, c, d, e) {
  return a * b + c * d - e;
}

const curriedComplex = curry(complexCalculation);

console.time('柯里化调用');
for (let i = 0; i < 10000; i++) {
  curriedComplex(i)(i+1)(i+2)(i+3)(i+4);
}
console.timeEnd('柯里化调用');

console.time('直接调用');
for (let i = 0; i < 10000; i++) {
  complexCalculation(i, i+1, i+2, i+3, i+4);
}
console.timeEnd('直接调用');

// 柯里化的优势总结
console.log('\n=== 柯里化优势 ===');
console.log('1. 参数复用 - 可以预先设置部分参数，创建更专用的函数');
console.log('2. 延迟执行 - 函数可以分步调用，直到所有参数都准备好');
console.log('3. 函数组合 - 便于进行函数组合和管道操作');
console.log('4. 代码复用 - 减少重复代码，提高代码可读性');

// 注意事项
console.log('\n=== 注意事项 ===');
console.log('1. 柯里化会增加函数调用栈深度，可能影响性能');
console.log('2. 需要明确函数的参数数量，对于可变参数函数需要特殊处理');
console.log('3. 占位符功能增加了复杂性，但提供了更大的灵活性');
console.log('4. 在实际项目中要考虑使用场景，避免过度柯里化');